import copy
import math
import time
from . import lib_MJ as MJ  # 使用的一些库函数
import logging
import datetime
import itertools

import yanchengMJ.global_variable as gv


class Node_Qidui:
    """
    七对牌型的树节点类，用于构建七对牌型的搜索树
    每个节点代表一种可能的牌型状态
    """
    def __init__(self,take=None, AA=[], T1=[], raw=[],taking_set=[], baida_num=0):
        """
        七对节点变量初始化
        :param take: 摸牌
        :param AA: 对子集合,每个对子表示为[card, card]
        :param T1: 单张牌集合
        :param raw: 待扩展集合
        :param taking_set: 已摸牌集合
        :param baida_num: 未使用的百搭数量
        """
        self.take = take
        self.AA=AA
        self.T1 = T1
        self.raw = raw
        self.taking_set = taking_set
        self.baida_num = baida_num
        self.children = []

    """
    添加子节点到当前节点

    Args:
        child: 要添加的子节点
    """
    def add_child(self,child):
        self.children.append(child)

    """打印当前节点的牌型信息"""
    def node_info(self):
        print ('AA:',self.AA,'T1:',self.T1,'raw:',self.raw,'taking_set:',self.taking_set,'baida_num',self.baida_num)

class Qidui:
    """
    七对牌型计算器
    用于评估七对牌型的可能性、生成牌型树并计算得分
    """
    def __init__(self,cards,suits,baida_card,padding=[], hua_num = 0):
        """
        七对类变量初始化
        :param cards: 手牌
        :param suits: 副露
        :param baida_card: 百搭牌
        :param fei_baida: 飞百搭数量
        :param padding: 填充牌，op操作时填充-1 ，一般来说，七对不会有这种操作
        """
        self.cards = cards
        self.suits = suits
        self.baida_card = baida_card
        self.discard_score = {}
        self.baida_num = cards.count(baida_card)
        self.padding=padding
        self.tree_list = []
        self.discard_state = {}
        self.hua_num = hua_num

    def qidui_CS(self):
        """
        计算七对牌型的初始组合状态

        Returns:
            list: [对子列表, 单张列表, 牌型难度值]
                对子列表: 已形成的对子
                单张列表: 剩余的单张牌
                牌型难度值: 评估形成七对的难度，值越小越容易形成
        """
        CS = [[],[],14]
        if self.suits!=[]:
            return CS
        cards_rm_baida = copy.copy(self.cards)
        for i in range(self.baida_num):
            cards_rm_baida.remove(self.baida_card)  #去除King的手牌
        for card in list(set(cards_rm_baida)):
            n = cards_rm_baida.count(card)
            if n==1:
                CS[1].append(card)
            elif n==2:
                CS[0].append([card,card])
            elif n==3:
                CS[0].append([card,card])
                CS[1].append(card)
            elif n==4:
                CS[0].append([card,card])
                CS[0].append([card,card])
        baida_num = self.baida_num
        # 这里把百搭用掉
        while baida_num>0:
            if len(CS[0]) + baida_num > 7:
                CS[0].append([self.baida_card,self.baida_card])
                baida_num-=2
            else:
                CS[0].append([0, 0])
                baida_num-=1
        CS[-1]-=len(CS[0])*2+(7-len(CS[0]))
        # CS[-1]+=2  # todo 这里给七对的xt+2，减少后面选择打七对的概率
        if CS[-1]>=4:  # todo  如果对子的数量过少，不建议打七对
            CS[-1]+=3
        if CS[-1]<0:
            CS[-1]=0
        return CS

    def expand_node(self,node):
        """
        扩展节点，生成子节点，构建七对牌型树

        Args:
            node: 当前要扩展的节点
        """
        # 与平胡类似，先生成待扩展集合，再进行节点扩展
        if len(node.AA)==7:  #胡牌判断 已经形成七对，停止拓展
            return
        else:
            if node.raw !=[]:
                # for card in node.raw:
                card = node.raw[-1]
                node.raw.pop()
                AA = copy.copy(node.AA)
                AA.append([card, card])
                taking_set = copy.copy(node.taking_set)
                taking_set.append(card)
                child = Node_Qidui(take=card, AA=AA, T1=node.T1, raw=node.raw,taking_set=taking_set, baida_num=node.baida_num)
                node.add_child(child=child)
                self.expand_node(node=child)
            else:
                if node.T1 != []:
                    t1_sets = copy.copy(node.T1)
                    # if -1 in t1_sets:
                    #     t1_sets.remove(-1)
                    T1 = copy.copy(node.T1)
                    for t1_set in itertools.combinations(t1_sets, min(7 - len(node.AA),len(t1_sets))):
                        node.T1 = copy.copy(T1)
                        node.raw = list(t1_set)
                        for t1 in node.raw:
                            node.T1.remove(t1)
                        self.expand_node(node=node)

    def generate_tree(self):
        """
        生成七对牌型的搜索树
        从初始状态开始，扩展所有可能的牌型组合
        """
        # 计算初始牌型组合
        CS = self.qidui_CS()
        # print "qidui CS",CS
        # 创建根节点
        node = Node_Qidui(take=None, AA=CS[0], T1=CS[1], taking_set=[], baida_num=self.baida_num)
        self.tree_list.append(node)
        self.expand_node(node=node)

    def qingYiSe(self, node):
        """
        检测是否为清一色牌型
        清一色指所有牌属于同一花色
        Args:
            node: 要检测的节点
        Returns:
            bool: 是否为清一色
        """
        w = 0 # 万
        ti = 0 # 条
        to = 0 # 桶

        # 检查所有对子的花色
        for t in node.AA:
            card = t[0]
            if card != 0:
                if card & 0xf0 == 0x00:
                    w = 1
                elif card & 0xf0 == 0x10:
                    ti = 1
                elif card & 0xf0 == 0x20:
                    to = 1
                else:
                    return False
        # 如果只有一种花色，则为清一色返回true
        if w + ti + to <= 1:
            return True
        else:
            return False

    def fan(self,node):
        """
        计算七对牌型的番数
        Args:
            node: 要计算的节点
        Returns:
            int: 番数
        """
        fan = 2 # 基础番数
        # 无花牌加倍
        if self.hua_num == 0:
            fan *= 2
        # 清一色四倍
        if self.qingYiSe(node):
            fan *= 4

        return fan

    def score(self, node):
        """
        计算七对牌型的得分（不包含杠分）
        Args:
            node: 要计算的节点
        Returns:
            int: 得分
        """
        score = 2 # 底分

        # 鸟分
        # global gv.LEFT_NUM
        if(sum(gv.LEFT_NUM) == 0) :
            score += 0
        else:
            # print(gv.LEFT_NUM)
            # print(sum(gv.LEFT_NUM))
            m = gv.LEFT_NUM[0] + gv.LEFT_NUM[4] + gv.LEFT_NUM[8] + gv.LEFT_NUM[9] + gv.LEFT_NUM[13] + gv.LEFT_NUM[17] \
                  + gv.LEFT_NUM[18] + gv.LEFT_NUM[22] + gv.LEFT_NUM[26] + gv.LEFT_NUM[27]
            score += m * 0 / sum(gv.LEFT_NUM)

        # 花分
        score += self.hua_num
        if self.hua_num == 4:
            score += 2

        # 牌型分
        wubaida_flag = True
        for aa in node.AA:
            # print()
            if aa == [0,0]:
                fangyang_flag = False

        if wubaida_flag:
            score += 1

        return score


    def evaluate(self,node):
        """
        评估节点价值，计算每种出牌的得分
        Args:
            node: 要评估的节点
        """
        if node.children==[]:
            if len(node.AA)==7:
                #node.node_info()
                taking_set_sorted = sorted(node.taking_set)
                value = 1
                for card in taking_set_sorted:
                    # print "card",card
                    if card == -1:
                        value = 1.0/34
                    else:
                        value *=gv.T_SELFMO[MJ.convert_hex2index(card)]
                fan = self.fan(node=node)
                score = self.score(node = node)
                #print('QD',fan,value)

                score = value * fan * score
                discards=node.T1+self.padding
                for discard in discards:
                    if discard not in self.discard_state.keys():
                        self.discard_state[discard]=[[],[]]
                        self.discard_state[discard][0].append(taking_set_sorted)
                        self.discard_state[discard][-1].append(score)
                    elif taking_set_sorted not in self.discard_state[discard][0]:
                        self.discard_state[discard][0].append(taking_set_sorted)
                        self.discard_state[discard][-1].append(score)
        else:
            for child in node.children:
                self.evaluate(child)

    def get_discard_score(self):
        """
        生成所有合理出牌的评估值
        Returns:
            dict: {出牌: 评估分数}
        """
        # t1 = time.time()
        # 生成牌型树
        self.generate_tree()
        # t2 = time.time()
        # 评估所有节点
        for tree in self.tree_list:
            self.evaluate(tree)
        # t3=time.time()
        # print ("qidui time",t2-t1,t3-t2)
        # 汇总每种出牌的总分数
        for discard in self.discard_state.keys():
            if discard not in self.discard_score:
                self.discard_score[discard] = 0
            self.discard_score[discard] = sum(self.discard_state[discard][-1])
        return self.discard_score